/*
 * InputStreamReaderThread.java
 * 
 * Copyright 2007 Kamen Petroff.
 * All Rights Reserved.
 * 
 * This software is the proprietary information of Kamen Petroff.
 * Use is subject to license terms.
 * 
 * Created on Mar 3, 2007 at 1:08:55 PM by Kamen Petroff
 */
package com.google.code.shell4eclipse.internal.process;

import java.io.IOException;
import java.io.InputStream;

import org.eclipse.core.runtime.IStatus;

import com.google.code.shell4eclipse.Shell4EclipsePlugin;
import com.google.code.shell4eclipse.process.IProcess;
import com.google.code.shell4eclipse.process.IStreamReader;
import com.google.code.shell4eclipse.process.IProcess.IStreamListener;

/**
 * 
 * 
 * @author Kamen Petroff 
 * @since Mar 3, 2007
 */
class InputStreamReader extends Thread implements IStreamReader {
	private InputStream inputStream;
	private IStreamListener listener;

	private int bufferLen = 1024*512;
	private byte[] buffer;
	private int pos = 0;
	private int readPos = 0;
	
	public InputStreamReader(InputStream inputStream, IProcess.IStreamListener listener) {
		this.inputStream = inputStream;
		this.listener = listener;
		this.buffer = new byte[bufferLen];
	}

	public void setListener(IStreamListener listener) {
		this.listener = listener;
	}
	
	public int getBufferLen() {
		return bufferLen;
	}
	
	public synchronized int read(byte[] buf) {
		int toRead = Math.min(buf.length, pos-readPos);
		
		if(toRead > 0) {
			System.arraycopy(buffer, readPos, buf, 0, toRead);
			readPos += toRead;
			this.notify();
		} else {
			pos = 0;
			readPos = 0;
		}
		
		return toRead;
	}
	
	private void notifyListener() {
		if(listener != null) {
			listener.onOutputAvailable(this);
		}
	}
	
	@Override
	public void run() {
		try{
			for(;;) {
				if(pos != buffer.length) {
					// block to read the next character
					int c = inputStream.read();
					if(c == -1) {
						break;
					}
					synchronized(this) {
						buffer[pos++] = (byte)c;
					}
				}
				
				synchronized(this) {
					// read available					
					int toRead = Math.min(inputStream.available(), buffer.length-pos);
					while(toRead > 0) {
						int read = inputStream.read(buffer, pos, toRead);
						pos += read;
						toRead = Math.min(inputStream.available(), buffer.length-pos);
					}
				}
				
				notifyListener(); // the listener should now read
				
				if(pos == buffer.length) {
					synchronized(this) {
						if(pos == buffer.length) { // buffer full, wait
							try{
								this.wait();
							}catch(InterruptedException e) {}
						}
					}
				}
			}
		} catch(IOException ex) {
			Shell4EclipsePlugin.log(IStatus.ERROR, "Failed to read shell's subprocess InputStream", ex);
		}
	}
}